package com.kurumi.autoprint.interceptor;

import org.aspectj.lang.annotation.Aspect;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.*;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * @description 注解声明式事务
 */
@Aspect
@Configuration
public class TxAdviceInterceptor {
	private static final int TX_METHOD_TIMEOUT = 50000;// 单位秒
	private static final String AOP_POINTCUT_EXPRESSION = "execution (* com.kurumi.autoprint.service.*.*(..))";

	@Autowired
	@Qualifier("transactionManager")
	private PlatformTransactionManager transactionManager;

	@Bean
	public TransactionInterceptor txAdvice() {
		NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
		/* 只读事务，不做更新操作 */
		RuleBasedTransactionAttribute readOnlyTx = new RuleBasedTransactionAttribute();
		readOnlyTx.setReadOnly(true);
		readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);
		/* 当前存在事务就使用当前事务，当前不存在事务就创建一个新的事务 */
		RuleBasedTransactionAttribute requiredTx = new RuleBasedTransactionAttribute();
		/* 抛出异常后执行切点回滚 */
		requiredTx.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
		requiredTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
		/* 设置事务失效时间，如果超过5秒，则回滚事务 */
		requiredTx.setTimeout(TX_METHOD_TIMEOUT);
		Map<String, TransactionAttribute> txMap = new HashMap<>();
		txMap.put("add*", requiredTx);
		txMap.put("save*", requiredTx);
		txMap.put("insert*", requiredTx);
		txMap.put("update*", requiredTx);
		txMap.put("delete*", requiredTx);
		txMap.put("get*", readOnlyTx);
		txMap.put("select*", readOnlyTx);
		txMap.put("query*", readOnlyTx);
		source.setNameMap(txMap);
		TransactionInterceptor txAdvice = new TransactionInterceptor(transactionManager, source);
		return txAdvice;
	}


	 /**
     * @author yc
     * @description 利用AspectJExpressionPointcut设置切面=切点+通知（写成内部bean的方式）
     * 
     */
	@Bean
	public Advisor txAdviceAdvisor() {
		/*
		 * 声明切点的面 切面（Aspect）：切面就是通知和切入点的结合。通知和切入点共同定义了关于切面的全部内容——它的功能、在何时和何地完成其功能。
		 */
		AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
		  /*声明和设置需要拦截的方法,用切点语言描写*/
		pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
		  /*设置切面=切点pointcut+通知TxAdvice*/
		return new DefaultPointcutAdvisor(pointcut, txAdvice());
	}
}
